package com.dems.admin.filter;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import com.dems.admin.security.SecurityUtils;
import com.dems.core.exception.LoanassistRuntimeException;
import com.dems.dao.dataobject.Menu;
import com.dems.dao.dataobject.User;
import com.dems.dao.mapper.la.MenuMapper;
import com.dems.domain.JsonResponse;
import com.dems.core.enumerate.ResEnum;
import com.dems.domain.constant.DianConstant;
import com.dems.domain.enumerate.UserTypeEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.Charsets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法
 * attemptAuthentication ：接收并解析用户凭证。
 * successfulAuthentication ：用户成功登录后，这个方法会被调用，我们在这个方法里生成token。
 *
 * @author yiliaohong on 2019/04/13.
 */

@Slf4j
@RefreshScope
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Value("${jwt.secret}")
    private String tokenSecret;

    @Value("${jwt.expireTime}")
    private Integer tokenExpireTime = (60 * 24);

    @Value("${la.h5.url}")
    private String H5_URL;

    @Value("${la.config.partnerId}")
    private Integer LA_CONFIG_PARTNERID;

    @Autowired
    private MenuMapper menuMapper;

    private AuthenticationManager authenticationManager;

    public JWTLoginFilter(AuthenticationManager authenticationManager, MenuMapper menuMapper, String tokenHeader, String tokenHead,
                          String tokenSecret, Integer tokenExpireTime, String h5Url) {
        this.authenticationManager = authenticationManager;
        this.menuMapper = menuMapper;
        this.tokenHeader = tokenHeader;
        this.tokenHead = tokenHead;
        this.tokenSecret = tokenSecret;
        this.tokenExpireTime = tokenExpireTime;
        this.H5_URL = h5Url;
    }

    /**
     * 接收并解析用户凭证
     *
     * @param req
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse response) throws AuthenticationException {
        try {

            // 校验验证码
            String vcode = Objects.toString(req.getSession().getAttribute("vcode"), "");
            if (StringUtils.isEmpty(vcode)) {
                throw new LoanassistRuntimeException("图片验证码不正确！");
            }
            User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
            if (StringUtils.isEmpty(user.getVcode()) || !vcode.equalsIgnoreCase(user.getVcode())) {
                throw new LoanassistRuntimeException("图片验证码不正确！");
            }
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getPhoneNo(),
                            user.getPassword(),
                            new ArrayList<>())
            );
        } catch (Exception e) {
            log.error("登录失败", e);

            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            try {
                JsonResponse error = JsonResponse.fail();
                error.setCode(ResEnum.AUTH_ERROR.getCode());
                if (e.getMessage().contains("Exception")) {
                    error.setMessage(ResEnum.AUTH_ERROR.getMsg());
                } else {
                    error.setMessage(e.getMessage());
                }

                response.getWriter().print(JSONObject.toJSONString(error));
            } catch (IOException e1) {
                log.error("登录异常", e1);
            }
        }
        return null;
    }

    /**
     * 用户成功登录后，这个方法会被调用，我们在这个方法里生成token
     *
     * @param request
     * @param response
     * @param chain
     * @param auth
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        // builder the token
        String token = null;
        try {
            Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
            // 定义存放角色集合的对象
            List<String> roleList = new ArrayList<>();
            for (GrantedAuthority grantedAuthority : authorities) {
                roleList.add(grantedAuthority.getAuthority());
            }
            Calendar calendar = Calendar.getInstance();
            Date now = calendar.getTime();
            // 设置签发时间
            calendar.setTime(new Date());
            // 设置过期时间
            calendar.add(Calendar.MINUTE, tokenExpireTime);// 10分钟
            Date time = calendar.getTime();
            token = Jwts.builder()
                    .setSubject(auth.getName() + "-" + roleList)
                    .setIssuedAt(now)//签发时间
                    .setExpiration(time)//过期时间
                    //采用什么算法是可以自己选择的，不一定非要采用HS512
                    .signWith(SignatureAlgorithm.HS512, Base64.getEncoder()
                            .encodeToString(tokenSecret.getBytes(Charsets.UTF_8)))
                    .compact();
            // 登录成功后，返回token到header里面
            response.addHeader(tokenHeader, tokenHead + token);

            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

            JSONObject userResVO = new JSONObject();
            User loanassistUser = SecurityUtils.getLoanassistUser();

            List<Menu> menuList = menuMapper.selectByRoleCode(loanassistUser.getRoleCodeList());
            if (CollectionUtils.isNotEmpty(menuList)) {
                loanassistUser.setMenuCodeList(menuList.stream().map(Menu::getMenuCode).collect(Collectors.toList()));
            }

            userResVO.put("phoneNo", loanassistUser.getPhoneNo());
            userResVO.put("nickname", loanassistUser.getNickname());
            userResVO.put("headimgUrl", loanassistUser.getHeadimgUrl());
            userResVO.put("token", tokenHead + token);
            userResVO.put("openId", loanassistUser.getOpenId());
            userResVO.put("userId", loanassistUser.getUserId());
            userResVO.put("name", loanassistUser.getName());
            userResVO.put("userType", loanassistUser.getUserType());
            userResVO.put("roleCodeList", loanassistUser.getRoleCodeList());
            userResVO.put("menuCodeList", loanassistUser.getMenuCodeList());
            userResVO.put("email", loanassistUser.getEmail());
            userResVO.put("workCity", loanassistUser.getWorkCity());
            userResVO.put("country", loanassistUser.getCountry());
            userResVO.put("province", loanassistUser.getProvince());
            Integer buId = loanassistUser.getBuId();
            userResVO.put("buId", buId);
            userResVO.put("creditAccount", loanassistUser.getCreditAccount());
            userResVO.put("personIntroduction", loanassistUser.getPersonIntroduction());
            userResVO.put("inviteUrl", H5_URL + DianConstant.INVITE_URL_PREFIX + loanassistUser.getPartnerCode());
            if (UserTypeEnum.checkTypeRole(loanassistUser.getUserType(), loanassistUser.getRoleCodeList(), UserTypeEnum.L1,UserTypeEnum.VL, UserTypeEnum.BIZ_VISIT_L, UserTypeEnum.T, UserTypeEnum.M)) {
                userResVO.put("parterUrl", H5_URL + DianConstant.PARTER_URL_PREFIX + loanassistUser.getPartnerCode());
            }
            String profile = loanassistUser.getProfile();
            if (StringUtils.isNotBlank(profile)) {
                JSONObject profileJson = JSONObject.parseObject(profile);
                userResVO.put("sipAccount", profileJson.getString("sipAccount"));
                userResVO.put("sipPassword", profileJson.getString("sipPassword"));

            }

            JsonResponse<JSONObject> jsonResponse = new JsonResponse<>(userResVO);
            response.getWriter().print(JSONObject.toJSONString(jsonResponse));
        } catch (Exception e) {
            log.error("[JWTLoginFilter]successfulAuthentication | error | ", e);
        }
    }

}